Streamlining Authentication and Globalization with Next-Auth and Next-Intl

Durgaprasad Budhwani
DCoderAI
Published in
6 min readFeb 20, 2024

Next.js continues to revolutionize web development with its versatile features. One of its latest additions, the Next.js App Router, brings forth robust support for React Server Components and opens up new avenues for efficient internationalization (i18n) handling on the server side. In this guide, we’ll explore how to seamlessly integrate i18n into your Next.js applications using next-intl.

Next.js
So, you’ve heard about Next.js, right? Imagine it as a supercharged version of React, but with some cool tricks up its sleeve. It’s like your React app got a major upgrade. With Next.js, you can make your app show up on the web faster with server-side magic (SSR), split your code like a ninja, and navigate around your app without breaking a sweat. It’s pretty much the go-to for anyone wanting to build slick, fast websites that both users and search engines love.

Next-Auth
Now, onto Next-Auth. Think of it as the bouncer for your Next.js club. It checks IDs at the door, letting you hook up all kinds of login methods, from social media logins to good old username and password, without making you sweat the security stuff. It’s like having your own security team, making sure only the right folks get into your app party.

Next-Intl
And then there’s Next-Intl, your personal translator for the web. It’s all about making sure your app speaks everyone’s language, literally. With this little gem, you can switch up content based on where your users are from, making your app a global superstar. It’s the perfect tool for anyone looking to welcome users from all over the planet.

Unique Middleware, Unique Solutions: Tailoring Technology to Tackle Distinct Challenges:

When developing applications with NextJS, managing middleware for authentication and internationalization separately can present a challenge, especially when striving for a streamlined and efficient codebase. The issue at hand involves integrating two distinct middleware libraries, next-auth/middleware for authentication (Next-Auth) and next-intl/middleware for internationalization (Next-Intl), into a single, cohesive middleware structure. This is crucial since NextJS encourages the use of only one root-level middleware to ensure optimal performance and simplicity in the application architecture.

The presented solution ingeniously combines both authentication and internationalization middleware into a unified middleware function. This approach not only simplifies the application’s structure but also maintains the distinct functionalities of both libraries without sacrificing their individual capabilities. Let’s dissect the given code snippet to understand how this integration is achieved:

import {NextRequest} from 'next/server';
import {withAuth} from 'next-auth/middleware';
import createIntlMiddleware from 'next-intl/middleware';

These lines import necessary modules and middleware for the functionality of the Next.js application:

  • NextRequest is imported from 'next/server'. This likely provides type definitions for the request object.
  • withAuth is imported from 'next-auth/middleware'. It is a middleware used for authentication purposes.
  • createIntlMiddleware is imported from 'next-intl/middleware'. This middleware is used to handle internationalization in the application.
const locales = ['en', 'de'];

const intlMiddleware = createIntlMiddleware({
locales,
localePrefix: 'as-needed',
defaultLocale: 'en'
});

Here, locales is defined as an array containing the supported languages, English ('en') and German ('de'). Then, intlMiddleware is created using createIntlMiddleware function, configured with options specifying supported locales, localePrefix, and defaultLocale

const publicPages = [
'/',
'/login',
'/sentry-example-page'
];

publicPages array is defined, which contains the paths of pages that should be accessible without authentication. These paths include the root path ('/'), '/login', and '/sentry-example-page'.

const authMiddleware = withAuth(
(req) => intlMiddleware(req),
{
secret: process.env.NEXTAUTH_SECRET,
callbacks: {
authorized: async ({ token }) => !!token,
},
pages: {
signIn: '/login',
}
});

authMiddleware is defined using the withAuth middleware function. It is configured with options including a secret for authentication, callbacks for handling authorization logic (in this case, it checks if a token exists), and pages for specifying sign-in page ('/login').

export default function middleware(req: NextRequest) {
const publicPathnameRegex = RegExp(
`^(/(${locales.join('|')}))?(${publicPages
.flatMap((p) => (p === '/' ? ['', '/'] : p))
.join('|')})/?$`,
'i'
);
const isPublicPage = publicPathnameRegex.test(req.nextUrl.pathname);

if (isPublicPage) {
return intlMiddleware(req);
} else {
return (authMiddleware as any)(req);
}
}
  • export default function middleware(req: NextRequest) { ... }: This line exports a function named middleware as the default export of the module. The function takes a parameter req of type NextRequest, which represents the request object.
  • const publicPathnameRegex = RegExp(/* regex pattern */, 'i');: This line defines a regular expression publicPathnameRegex using the RegExp constructor. The regular expression pattern is dynamically constructed using template literals and includes paths from publicPages array and supported locales (locales). The 'i' flag at the end makes the regular expression case-insensitive.
  • const isPublicPage = publicPathnameRegex.test(req.nextUrl.pathname);: This line tests whether the requested URL's pathname matches the regular expression publicPathnameRegex. If it does, it indicates that the requested page is a public page.
  • if (isPublicPage) { ... } else { ... }: This conditional statement checks if the requested page is a public page or not. If it is a public page, the internationalization middleware (intlMiddleware) is applied to the request using intlMiddleware(req). If it is not a public page, the authentication middleware (authMiddleware) is applied to the request.
export const config = {
// Skip all paths that should not be internationalized
matcher: ['/((?!api|_next|.*\\..*).*)']
};

export const config = { ... }: This line exports a configuration object named config. This object specifies a matcher property, which contains an array of regex patterns. These patterns are used to skip paths that should not undergo internationalization. In this case, it skips paths containing 'api', '_next', or any path with a file extension.

Full Code:

import {NextRequest} from 'next/server';
import {withAuth} from 'next-auth/middleware';
import createIntlMiddleware from 'next-intl/middleware';

const locales = ['en', 'de'];

const intlMiddleware = createIntlMiddleware({
locales,
localePrefix: 'as-needed',
defaultLocale: 'en'
});

const publicPages = [
'/',
'/login',
'/sentry-example-page'
];

const authMiddleware =withAuth(
(req) => intlMiddleware(req),
{
secret: process.env.NEXTAUTH_SECRET,
callbacks: {
authorized: async ({ token }) => !!token,
},
pages: {
signIn: '/login',
}
});


export default function middleware(req: NextRequest) {
const publicPathnameRegex = RegExp(
`^(/(${locales.join('|')}))?(${publicPages
.flatMap((p) => (p === '/' ? ['', '/'] : p))
.join('|')})/?$`,
'i'
);
const isPublicPage = publicPathnameRegex.test(req.nextUrl.pathname);

if (isPublicPage) {
return intlMiddleware(req);
} else {
return (authMiddleware as any)(req);
}
}

export const config = {
// Skip all paths that should not be internationalized
matcher: ['/((?!api|_next|.*\\..*).*)']
};

Conclusion:

In conclusion, this blog post demonstrates the seamless integration of authentication and globalization features into Next.js applications using Next-Auth and Next-Intl, respectively. Next.js provides a powerful framework for web development, offering support for React Server Components and efficient internationalization handling on the server side. Next-Auth acts as a robust authentication middleware, allowing developers to implement various login methods securely. On the other hand, Next-Intl facilitates language localization, ensuring that applications can cater to users from diverse linguistic backgrounds.

The integration of Next-Auth and Next-Intl presents a unique challenge of managing middleware for authentication and internationalization separately. To address this challenge, the blog proposes a solution that combines both middleware libraries into a unified middleware function. This approach simplifies the application structure while preserving the distinct functionalities of Next-Auth and Next-Intl.

By dissecting the provided code snippet, the blog elucidates the implementation details of the unified middleware function. It imports necessary modules and middleware, defines supported locales and public pages, configures authentication middleware with appropriate options, and constructs a regular expression to determine whether a requested page is public or requires authentication. Additionally, it exports a configuration object specifying paths to skip for internationalization.

Overall, the blog provides comprehensive guidance for developers looking to enhance their Next.js applications with authentication and globalization features, showcasing how Next-Auth and Next-Intl can be seamlessly integrated to address distinct challenges and streamline the development process.

--

--

Durgaprasad Budhwani
DCoderAI

Chief Technologist @ Tech9 | Udemy Instructor | Cloud Expert | JS | React | Go | NodeJs | Youtuber | Serverless | DevOps | 2 x AWS | Azure | Google Cloud | CKAD