Closed amannn closed 1 year ago
@amannn don't have time to handle this right now, will open a PR within the next couple of days. Assuming you want it targeting the main
branch?
So if you're going to chain middleware it needs to go last, and using the API they expose is probably going to result in the least severe concussion from banging your head against the table.
@narakhan Hope you're recovering well currently! 😄
Thank you so much for chiming in here, if you're interested to look into this I'd be more than happy! 🙌
I'm currently thinking about a section in the middleware docs where a) we explain conceptually what it means to compose middleware (maybe similar to https://github.com/amannn/next-intl/pull/149#issuecomment-1471592626) and then b) a guide on how to integrate with Auth.js, since it's the most popular middleware for Next.js as far as I know.
Optionally, we could also add and link to an example in the repository (could be based on example-next-13
). This could be used for simple e2e tests too if that helps, also to stay in sync with future releases of Auth.js.
Assuming you want it targeting the
main
branch?
Yep, I've recently cleaned up the RSC branch to only contain code specifically for RSC. Other improvements like the middleware are now available in stable releases and the documentation is also deployed on main
.
I just had a moment to experiment with composing middlewares. This code seems to be working:
import createMiddleware from 'next-intl/middleware';
import {NextFetchEvent, NextMiddleware, NextRequest} from 'next/server';
export default withExtraMiddleware(
createMiddleware({
locales: ['en', 'de'],
defaultLocale: 'en'
})
);
function withExtraMiddleware(next: NextMiddleware) {
return async (request: NextRequest, event: NextFetchEvent) => {
// Step 1: Potentially change the incoming request
request.headers.set('x-test', 'test');
// Step 2: Call the nested next-intl middleware
const response = next(request, event);
// Step 3: Potentially change the response
if (response) {
response.headers.set('x-test', 'test');
}
return response;
};
}
export const config = {
// Skip all paths that should not be internationalized
matcher: ['/((?!_next|.*\\..*).*)']
};
I also found there is a (slightly hacky) way to programmatically compose rewrites on top of next-intl
that could be interesting to implement named routes with localized pathnames:
// ...
// Step 2: Call the nested next-intl middleware
const response = next(request, event);
// Step 3: Potentially change the response
if (response) {
// Add a programmatic rewrite
// https://github.com/vercel/next.js/discussions/33477
if (request.nextUrl.pathname === '/de/über') {
const url = new URL('/de/about', request.url);
response.headers.set('x-middleware-rewrite', url.toString());
}
}
// ...
@narakhan Are you still interested on working on the Auth.js example? I could take care of the docs.
I just had a moment to experiment with composing middlewares. This code seems to be working:
import createMiddleware from 'next-intl/middleware'; import {NextFetchEvent, NextMiddleware, NextRequest} from 'next/server'; export default withExtraMiddleware( createMiddleware({ locales: ['en', 'de'], defaultLocale: 'en' }) ); function withExtraMiddleware(next: NextMiddleware) { return async (request: NextRequest, event: NextFetchEvent) => { // Step 1: Potentially change the incoming request request.headers.set('x-test', 'test'); // Step 2: Call the nested next-intl middleware const response = await next(request, event); // Step 3: Potentially change the response if (response) { response.headers.set('x-test', 'test'); } return response; }; } export const config = { // Skip all paths that should not be internationalized matcher: ['/((?!_next|.*\\..*).*)'] };
I also found there is a (slightly hacky) way to programmatically compose rewrites on top of
next-intl
that could be interesting to implement named routes with localized pathnames:// ... // Step 2: Call the nested next-intl middleware const response = await next(request, event); // Step 3: Potentially change the response if (response) { // Add a programmatic rewrite // https://github.com/vercel/next.js/discussions/33477 if (request.nextUrl.pathname === '/de/über') { const url = new URL('/de/about', request.url); response.headers.set('x-middleware-rewrite', url.toString()); } } // ...
@narakhan Are you still interested on working on the Auth.js example? I could take care of the docs.
Sorry likely don't have time to handle it right now.
I have the need to do something similar but my config for createIntlMiddleware
is dynamically fetched from the edge. I ended up with something like this but I am not convinced it is the right approach.
async function middleware(req: NextRequest): Promise<NextResponse | void> {
// Fetch config from edge
const config = await getConfigFromHost(req.nextUrl.host);
// Set custom header
const response = NextResponse.next();
if (config.id) {
response.headers.set('x-marketplace-id', config.id || 'unknown');
}
const intlMiddleware = createIntlMiddleware(config.intl);
const intlResponse = await intlMiddleware(req);
// Merge the headers from the response with the intlResponse headers
for (const [key, value] of response.headers.entries()) {
intlResponse.headers.set(key, value);
}
return intlResponse;
}
export default middleware
It is working with one es-lint error of Unexpected await of a non-Promise (non-"Thenable")
.
This could be a useful case to put in the doc's potentially if there is a best practice approach.
Thanks everyone for participating and especially @narakhan for sharing his middleware implementation for Auth.js! There are now new docs on composing middlewares, including an example for Auth.js.
@ryanburns23 The new docs should hopefully help with your use case, let me know if there are open questions!
Thanks everyone for participating and especially @narakhan for sharing his middleware implementation for Auth.js! There are now new docs on composing middlewares, including an example for Auth.js.
@ryanburns23 The new docs should hopefully help with your use case, let me know if there are open questions!
Thanks for the release, while browsing the new middleware documentation I noticed that the provided example repos doesn't exist (example repos)
EDIT: this is the example repos URL (it was redirecting to wrong branch) https://github.com/amannn/next-intl/tree/feat/next-13-rsc/packages/example-next-13-next-auth
@bilel-lm Oh, good point—thanks! I've moved some files today, the links should be fixed now.
Thanks everyone for participating and especially @narakhan for sharing his middleware implementation for Auth.js! There are now new docs on composing middlewares, including an example for Auth.js.
@ryanburns23 The new docs should hopefully help with your use case, let me know if there are open questions!
Hello @amannn I have been implementing some similar solutions, and I notice that the example does not redirect to "/secret" page after login. How would you suggest redirecting the user to "/secret" once logged in? I am having some issues with this implementation.
Thank you so much!
@guifeliper That's a good point, thanks for noticing!
I'm not really experienced with Auth.js, this PR was my only exposure so far to the library. It is sometimes a bit tricky to find the right config.
If you're interested in contributing, we've got a minimalistic example in this repo along with some tests:
That could be helpful to quickly iterate with different config and figure out a solution, if you like!
I'm so sorry to re-open this issue, but I'm having trouble understanding the underlying logic for middleware composition. I'm handling my cookie-based authentication through a custom middleware which looks like this:
import { NextRequest, NextResponse } from "next/server";
import { HOME_PATH, LOGIN_PATH, TERMS_PATH } from "constants/paths";
const accessTokenCookieName =
process.env.ACCESS_TOKEN_COOKIE_NAME ?? "hal.access-token";
const PUBLIC_PATHS = [LOGIN_PATH, TERMS_PATH];
/**
* Redirect to login page if no access token is found
*/
export function middleware(request: NextRequest) {
const token = request.cookies.get(accessTokenCookieName);
if (token) {
// redirect to homepage if token is found on login page
if (request.nextUrl.pathname === LOGIN_PATH) {
return NextResponse.redirect(new URL(HOME_PATH, request.nextUrl.origin));
}
return NextResponse.next();
}
// Skip middleware for the public pages
if (PUBLIC_PATHS.includes(request.nextUrl.pathname)) {
return NextResponse.next();
}
return NextResponse.redirect(new URL(LOGIN_PATH, request.nextUrl.origin));
}
export const config = {
/**
* Match all request paths except:
* - API
* - NextJS chunks
* - favicon.ico
*/
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};
I tried adapting it using the piece of code provided earlier in this issue, but I'm fairly new to next middleware usage, and I want to make sure I understand how it affects the response, and how I can combine both my authentication logic with the middleware provided by next-intl
Anyone have an example of integrating Supabase SSR cookie refreshing middleware with next-intl middleware when using Next.js 14?
Anyone have an example of integrating Supabase SSR cookie refreshing middleware with next-intl middleware when using Next.js 14?
Did you find a solution?
@AssisrMatheus @kimmo-koo maybe this will help you: https://github.com/amannn/next-intl/issues/719
I'm so sorry to re-open this issue, but I'm having trouble understanding the underlying logic for middleware composition. I'm handling my cookie-based authentication through a custom middleware which looks like this:
import { NextRequest, NextResponse } from "next/server"; import { HOME_PATH, LOGIN_PATH, TERMS_PATH } from "constants/paths"; const accessTokenCookieName = process.env.ACCESS_TOKEN_COOKIE_NAME ?? "hal.access-token"; const PUBLIC_PATHS = [LOGIN_PATH, TERMS_PATH]; /** * Redirect to login page if no access token is found */ export function middleware(request: NextRequest) { const token = request.cookies.get(accessTokenCookieName); if (token) { // redirect to homepage if token is found on login page if (request.nextUrl.pathname === LOGIN_PATH) { return NextResponse.redirect(new URL(HOME_PATH, request.nextUrl.origin)); } return NextResponse.next(); } // Skip middleware for the public pages if (PUBLIC_PATHS.includes(request.nextUrl.pathname)) { return NextResponse.next(); } return NextResponse.redirect(new URL(LOGIN_PATH, request.nextUrl.origin)); } export const config = { /** * Match all request paths except: * - API * - NextJS chunks * - favicon.ico */ matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"], };
I tried adapting it using the piece of code provided earlier in this issue, but I'm fairly new to next middleware usage, and I want to make sure I understand how it affects the response, and how I can combine both my authentication logic with the middleware provided by
next-intl
any updates ?
+1 @PhongLi I need updates too
Is your feature request related to a problem? Please describe.
There are other middlewares like Auth.js and it would be good to provide documentation on how to use the middleware from
next-intl
in combination with those.Describe the solution you'd like
A section in the middleware documentation.
Previous discussion:
/cc @narakhan @kieranm @velychkodmytro20