-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
provide an example with i18n #9536
Comments
Hi! After many hours I've managed to create my own auth middleware to work properly alongside next-intl. Here is the code: Middleware import { auth } from "@/auth";
import pages from "./lib/pages";
import { NextRequest, NextResponse } from "next/server";
import createIntlMiddleware from "next-intl/middleware";
const locales = ["en", "es"];
const protectedPages = ["/dashboard/*"];
const authPages = ["/auth/signin", "/auth/signup"];
const intlMiddleware = createIntlMiddleware({
locales,
defaultLocale: "es",
localePrefix: "as-needed",
});
const testPagesRegex = (pages: string[], pathname: string) => {
const regex = `^(/(${locales.join("|")}))?(${pages
.map((p) => p.replace("/*", ".*"))
.join("|")})/?$`;
return new RegExp(regex, "i").test(pathname);
};
const handleAuth = async (
req: NextRequest,
isAuthPage: boolean,
isProtectedPage: boolean,
) => {
const session = await auth();
const isAuth = !!session?.user;
if (!isAuth && isProtectedPage) {
let from = req.nextUrl.pathname;
if (req.nextUrl.search) {
from += req.nextUrl.search;
}
return NextResponse.redirect(
new URL(
`${pages.auth.signin()}?from=${encodeURIComponent(from)}`,
req.url,
),
);
}
if (isAuth && isAuthPage) {
return NextResponse.redirect(new URL(pages.dashboard.root, req.nextUrl));
}
return intlMiddleware(req);
};
export default async function middleware(req: NextRequest) {
const isAuthPage = testPagesRegex(authPages, req.nextUrl.pathname);
const isProtectedPage = testPagesRegex(protectedPages, req.nextUrl.pathname);
return await handleAuth(req, isAuthPage, isProtectedPage);
}
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
}; use of signIn() const searchParams = useSearchParams();
const handleGoogle = async () => {
setLoading(true);
return await signIn("google", {
callbackUrl: searchParams?.get("from") || pages.dashboard.root,
});
}; |
@jero237 How to deal with callbackUrl in signin?
this code alwalys redirect pages.dashboard.root |
great question, I've just updated the comment above |
@jero237 I change some code with signin after redirect by callbackUrl if (isAuth && isAuthPage) {
const url = req.nextUrl.clone();
const fromValue = url.searchParams.get("from");
return NextResponse.redirect(new URL(fromValue ?? "/", req.nextUrl));
} This is my all code const handleAuth = async (req: NextRequest, isAuthPage: boolean, isProtectedPage: boolean) => {
const session = await auth();
const isAuth = !!session?.user;
if (!isAuth && isProtectedPage) {
let from = req.nextUrl.pathname;
if (req.nextUrl.search) {
from += req.nextUrl.search;
}
return NextResponse.redirect(new URL(`/auth/signin?from=${encodeURIComponent(from)}`, req.url));
}
if (isAuth && isAuthPage) {
const url = req.nextUrl.clone();
const fromValue = url.searchParams.get("from");
return NextResponse.redirect(new URL(fromValue ?? "/", req.nextUrl));
}
return intlMiddleware(req);
};
export default async function middleware(req: NextRequest) {
const isAuthPage = testPagesRegex(authPages, req.nextUrl.pathname);
const isProtectedPage = testPagesRegex(protectedPages, req.nextUrl.pathname);
return await handleAuth(req, isAuthPage, isProtectedPage);
} |
Same requirement from i18next import { withAuth } from 'next-auth/middleware';
import acceptLanguage from 'accept-language';
const cookieName = 'i18next';
const fallbackLng = 'en';
const languages = ['en', 'zh'];
// Language from cookie/headers/fallback
function getLng(req: NextRequest) {
if (req.cookies.has(cookieName))
return acceptLanguage.get(req.cookies.get(cookieName)?.value);
return acceptLanguage.get(req.headers.get('Accept-Language')) ?? fallbackLng;
};
// Append i18n language to path like: app/[lng]/social-login/page.tsx
// example: /social-login -> /en/social-login
function handleI18n(req: NextRequest) {
if (req.nextUrl.pathname.startsWith('/api')) return;
if (
!languages.some(loc => req.nextUrl.pathname.startsWith(`/${loc}`)) &&
!req.nextUrl.pathname.startsWith('/_next')
) {
const lng = getLng(req);
return NextResponse.redirect(
new URL(`/${lng}${req.nextUrl.pathname}`, req.url)
);
}
if (req.headers.has('referer')) {
const refererUrl = new URL(req.headers.get('referer')!);
const lngInReferer = languages.find(l =>
refererUrl.pathname.startsWith(`/${l}`)
);
const response = NextResponse.next();
if (lngInReferer)
response.cookies.set(cookieName, lngInReferer, { sameSite: 'lax' });
return response;
}
}
function middleware(req: NextRequest) {
console.log('Running middleware!'); // <--------- This will not run if not authed
return handleI18n(req) ?? NextResponse.next();
}
export default withAuth(middleware, {
pages: {
signIn: '/social-login', // <--------- This will not redirect to /en/social-login
signOut: '/social-login',
},
});
export const config = {
matcher: ['/((?!_next/static|_next/image|assets|favicon.ico|sw.js|fonts).*)'],
}; will be nice if we can access req in NextAuthMiddlewareOptions export default withAuth(middleware, (req) => ({
pages: {
signIn: `${getLng(req)}/social-login`,
signOut: `${getLng(req)}/social-login`,
},
})); |
update: import acceptLanguage from 'accept-language';
import { withAuth, type NextRequestWithAuth } from 'next-auth/middleware';
import { NextResponse, type NextRequest } from 'next/server';
import { cookieName, fallbackLng, languages } from './i18n';
acceptLanguage.languages([...languages]);
export const config = {
matcher: [
'/((?!_next/static|_next/image|assets|favicon.ico|sw.js|manifest.json|fonts).*)',
],
};
const getLng = (req: NextRequest) => {
if (req.cookies.has(cookieName))
return acceptLanguage.get(req.cookies.get(cookieName)?.value);
return acceptLanguage.get(req.headers.get('Accept-Language')) ?? fallbackLng;
};
const handleI18n = (req: NextRequest) => {
if (req.nextUrl.pathname.startsWith('/api')) return;
const pathnameStartsWithLanguage = languages.some(loc =>
req.nextUrl.pathname.startsWith(`/${loc}`)
);
if (
!pathnameStartsWithLanguage &&
!req.nextUrl.pathname.startsWith('/_next')
) {
const lng = getLng(req);
return NextResponse.redirect(
new URL(`/${lng}${req.nextUrl.pathname}`, req.url)
);
}
if (req.headers.has('referer')) {
const refererUrl = new URL(req.headers.get('referer')!);
const lngInReferer = languages.find(l =>
refererUrl.pathname.startsWith(`/${l}`)
);
const response = NextResponse.next();
if (lngInReferer) {
response.cookies.set(cookieName, lngInReferer, { sameSite: 'lax' });
}
return response;
}
return;
};
const handleAuth = (req: NextRequestWithAuth) => {
if (req.nextUrl.pathname.includes('/dmz')) return NextResponse.next();
const token = req.nextauth.token;
if (!token)
return NextResponse.redirect(new URL(`/dmz/social-login`, req.url));
return;
};
const middleware = (req: NextRequestWithAuth) => {
const i18nRes = handleI18n(req);
if (i18nRes) return i18nRes;
const authRes = handleAuth(req);
if (authRes) return authRes;
return NextResponse.next();
};
export default withAuth(middleware, {
callbacks: {
authorized() {
/**
* Trick, tell next-auth we are always authorized, so middleware callback will not skip.
* then we handle token check and redirect to login page our self.
*/
return true;
},
},
}); |
@balazsorban44 save us please 🥺 |
There is an example by the maintainer of next-intl https://github.com/amannn/next-intl/blob/main/examples/example-app-router-next-auth/src/middleware.ts (for v4) |
This comment has been minimized.
This comment has been minimized.
I left the ship for Lucia Auth, no more issue but thanks :) |
What is the improvement or update you wish to see?
it would be super helpful to have an official example repo which shows how to combine
next-auth
v5 with an i18n library likenext-intl
.from the current docs, it is not clear to me
thanks 🙏
Is there any context that might help us understand?
Does the docs page already exist? Please link to it.
No response
The text was updated successfully, but these errors were encountered: