-
Notifications
You must be signed in to change notification settings - Fork 10
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
Combine createMiddleware with additional auth middleware #2639
Comments
Hey @brandanking-decently - our // This example is for Auth.js 5, the successor to NextAuth 4
import arcjet, { createMiddleware, shield } from "@arcjet/next";
import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";
// @ts-ignore
import type { NextAuthConfig, NextAuthRequest } from "next-auth";
export const config = {
// matcher tells Next.js which routes to run the middleware on.
// This runs the middleware on all routes except for static assets.
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
const aj = arcjet({
key: process.env.ARCJET_KEY!, // Get your site key from https://app.arcjet.com
rules: [
// Protect against common attacks with Arcjet Shield
shield({
mode: "LIVE", // will block requests. Use "DRY_RUN" to log only
}),
],
});
export const authConfig = {
providers: [GitHub],
} satisfies NextAuthConfig;
const { auth } = NextAuth(authConfig);
export const authMiddleware = auth(async (req: NextAuthRequest) => {
if (!req.auth) {
// If the user is not authenticated, return a 401 Unauthorized response. You
// may wish to redirect to a login page instead.
return Response.json({ message: "Unauthorized" }, { status: 401 });
}
});
export default createMiddleware(aj, authMiddleware); For multiple middleware then you may want to use a helper library like https://nemo.rescale.build Have you tried either of these approaches? Did you have any problems? |
I was trying to use the @nosecone/next package which doesn't seem to accept another middleware into it? Is this achievable in that package? |
Ah, for Nosecone you would need to call it within your own custom middleware function. If you're using Auth.js with Next.js then you could create a import { type NoseconeOptions, createMiddleware, defaults } from "@nosecone/next";
import { auth } from "auth";
// Nosecone security headers configuration
// https://docs.arcjet.com/nosecone/quick-start
const noseconeOptions: NoseconeOptions = {
...defaults,
};
const securityHeaders = createMiddleware(noseconeOptions);
export default auth(async (req) => {
if (!req.auth && !req.nextUrl.pathname.startsWith("/auth")) {
const newUrl = new URL("/auth/signin", req.nextUrl.origin)
return Response.redirect(newUrl)
}
return securityHeaders();
})
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
} This assumes Auth.js 5 beta and that the auth API path is Does that work? |
My middleware looks like this, we also use Next Intl to handle i18n. Not sure this is possible to chain currently export default auth(
async function middleware(request: KindeRequest) {
return intlMiddleware(request);
},
); |
Chaining more than 2 layers of middleware gets fiddly, so I'd suggest using https://nemo.rescale.build/ to have Nosecone run "before", then set up auth, and have the intlMiddleware run "after". |
I believe I was able to get it close to working based on what you said, however, whenever I use the headerMiddleware (needed to rename as multiple createMiddleware functions) I now always get a 404. Do you have any idea what might be causing it? const intlMiddleware = createIntlMiddleware(routing);
const noseconeOptions: NoseconeOptions = {
...defaults,
};
const before: MiddlewareFunction[] = [headerMiddleware(noseconeOptions)];
const after: MiddlewareFunction[] = [
async ({ request }: MiddlewareFunctionProps) => {
return withAuth(request, { isReturnToCurrentPage: true, publicPaths: ['/'] });
},
async ({ request }: MiddlewareFunctionProps) => {
return intlMiddleware(request);
},
];
export const middleware = createMiddleware({}, { before, after });
export const config = { matcher: ['/((?!api|_next|.*\\..*).*)'] }; |
Can you provide the imports and package.json so I can see which packages these are coming from please? |
import { withAuth } from '@kinde-oss/kinde-auth-nextjs/middleware';
import { type NoseconeOptions, defaults, createMiddleware as headerMiddleware } from '@nosecone/next';
import { type MiddlewareFunction, type MiddlewareFunctionProps, createMiddleware } from '@rescale/nemo';
import createIntlMiddleware from 'next-intl/middleware';
import { routing } from './i18n/routing';
const intlMiddleware = createIntlMiddleware(routing);
const noseconeOptions: NoseconeOptions = {
...defaults,
};
const before: MiddlewareFunction[] = [headerMiddleware(noseconeOptions)];
const after: MiddlewareFunction[] = [
async ({ request }: MiddlewareFunctionProps) => {
return withAuth(request, { isReturnToCurrentPage: true, publicPaths: ['/'] });
},
async ({ request }: MiddlewareFunctionProps) => {
return intlMiddleware(request);
},
];
export const middleware = createMiddleware({}, { before, after });
export const config = { matcher: ['/((?!api|_next|.*\\..*).*)'] }; |
I had a play around with Nemo and couldn't get it to work properly either. We have an internal tool that handles the basic Nemo functionality to chain middleware, so I've tidied that up here. Can you try this please: 1. import { type NoseconeOptions, defaults, createMiddleware as noseconeMiddleware } from "@nosecone/next";
import {
type NextFetchEvent,
type NextMiddleware,
type NextRequest,
NextResponse,
} from "next/server";
import { match } from "path-to-regexp";
// Next.js middleware config
export const config = {
matcher: ['/((?!_next/|_static|_vercel|[\\w-]+\\.\\w+).*)'],
};
// Nosecone security headers configuration
// https://docs.arcjet.com/nosecone/quick-start
const noseconeOptions: NoseconeOptions = {
...defaults,
};
const securityHeaders = noseconeMiddleware(noseconeOptions);
// Add any paths you want to run different middleware for. They use
// path-to-regexp which is the same as the Next.js config. You can provide a
// single middleware or an array of middlewares.
export default router({
// Run nosecone middleware on any path
"/{*path}": [securityHeaders],
});
// Simplified version of nemo. This could be extracted into a utility library
function router(
pathMiddlewareMap: Record<string, NextMiddleware | NextMiddleware[]>,
): NextMiddleware {
const middleware = Object.entries(pathMiddlewareMap).map(
([path, middleware]) => {
if (Array.isArray(middleware)) {
return [match(path), middleware] as const;
} else {
return [match(path), [middleware]] as const;
}
},
);
return async (
request: NextRequest,
event: NextFetchEvent,
): Promise<NextResponse | Response> => {
const path = request.nextUrl.pathname || "/";
const addedHeaders = new Headers();
for (const [matchFunc, middlewareFuncs] of middleware) {
const m = matchFunc(path);
if (m) {
for (const fn of middlewareFuncs) {
const resp = await fn(request, event);
// TODO: better response guards
if (typeof resp !== "undefined" && resp !== null) {
resp.headers.forEach((value, key) => {
addedHeaders.set(key, value);
});
}
}
}
}
addedHeaders.set("x-middleware-next", "1");
return new Response(null, {
headers: addedHeaders,
});
};
}
export default router({
// Run nosecone middleware on any path
"/{*path}": [securityHeaders, intlMiddleware]
}); If this works for you then I'll update our docs to include it as a proper example. |
I was wondering how I can combine the createMiddleware method with other middleware such as NextAuth
The text was updated successfully, but these errors were encountered: