Skip to content
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

chore: Add docs for chaining Nosecone middleware #328

Merged
merged 1 commit into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/content/docs/nosecone/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,19 @@ import StaticOptOut from "@/snippets/nosecone/reference/next-js/StaticOptOut.mdx

<StaticOptOut />

Additional resources:
### Chaining middleware

If you are using multiple Next.js middleware functions, you can chain them
together to add the security headers.

This example shows how to chain Arcjet with [Auth.js](https://authjs.dev)
middleware:

import ChainedMiddleware from "@/snippets/nosecone/reference/next-js/ChainedMiddleware.mdx";

<ChainedMiddleware />

### Additional resources

- [Next.js Content-Security-Policy guide](https://nextjs.org/docs/app/building-your-application/configuring/content-security-policy)

Expand Down
21 changes: 14 additions & 7 deletions src/snippets/nosecone/quick-start/next-js/Step2.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Code } from "@astrojs/starlight/components";
import { Aside, Code } from "@astrojs/starlight/components";
import SelectableContent from "@/components/SelectableContent";

import Step2MiddlewareTS from "./Step2Middleware.ts?raw";
Expand All @@ -12,12 +12,6 @@ import Step2LayoutNoCacheJS from "./Step2LayoutNoCache.js?raw";
Nosecone applies headers to all your routes via middleware in your Next.js
application.

:::note
We recommend you remove any `export const config = ...` from your middleware so
Nosecone will run on every route. This ensures the headers are applied to all
requests.
:::

<SelectableContent client:load syncKey="language" frameworkSwitcher>
<div slot="TS" slotIdx="1">
<Code code={Step2MiddlewareTS} lang="ts" title="middleware.ts" />
Expand All @@ -27,6 +21,10 @@ requests.
</div>
</SelectableContent>

We recommend you remove any `export const config = ...` from your middleware so
Nosecone will run on every route. This ensures the headers are applied to all
requests.

You also need to opt-out of static generation in your application. See the
`@nosecone/next` [reference
guide](/nosecone/reference#nextjs-security-headers-middleware) for more details
Expand Down Expand Up @@ -68,3 +66,12 @@ To opt-out of static generation, modify your layout file with the following:
/>
</div>
</SelectableContent>

We recommend you remove any `export const config = ...` from your middleware so
Nosecone will run on every route. This ensures the headers are applied to all
requests.

<Aside type="note" title="Chaining middleware">
See the [reference guide](/nosecone/reference#chaining-middleware) if you need
to chain multiple middleware functions.
</Aside>
27 changes: 27 additions & 0 deletions src/snippets/nosecone/reference/next-js/ChainedMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { createMiddleware, defaults } from "@nosecone/next";
import { auth } from "./auth";

// Nosecone security headers configuration
// https://docs.arcjet.com/nosecone/quick-start
const noseconeOptions = {
...defaults,
};

const securityHeaders = createMiddleware(noseconeOptions);

// @ts-ignore: This type will be correct when used in a real app
export default auth(async (req) => {
// Redirect to signin page if not authenticated
// Example from https://authjs.dev/getting-started/session-management/protecting
if (!req.auth && !req.nextUrl.pathname.startsWith("/auth")) {
const newUrl = new URL("/auth/signin", req.nextUrl.origin);
return Response.redirect(newUrl);
}

// Otherwise return security headers
return securityHeaders();
});

export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};
14 changes: 14 additions & 0 deletions src/snippets/nosecone/reference/next-js/ChainedMiddleware.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Code } from "@astrojs/starlight/components";
import SelectableContent from "@/components/SelectableContent";

import ChainedMiddlewareTS from "./ChainedMiddleware.ts?raw";
import ChainedMiddlewareJS from "./ChainedMiddleware.js?raw";

<SelectableContent client:load syncKey="language">
<div slot="TS" slotIdx="1">
<Code code={ChainedMiddlewareTS} lang="ts" title="middleware.ts" />
</div>
<div slot="JS" slotIdx="2">
<Code code={ChainedMiddlewareJS} lang="js" title="middleware.js" />
</div>
</SelectableContent>
32 changes: 32 additions & 0 deletions src/snippets/nosecone/reference/next-js/ChainedMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
type NoseconeOptions,
createMiddleware,
defaults,
} from "@nosecone/next";
// @ts-ignore: Import your auth library. See https://authjs.dev
import { auth } from "./auth";

// Nosecone security headers configuration
// https://docs.arcjet.com/nosecone/quick-start
const noseconeOptions: NoseconeOptions = {
...defaults,
};

const securityHeaders = createMiddleware(noseconeOptions);

// @ts-ignore: This type will be correct when used in a real app
export default auth(async (req) => {
// Redirect to signin page if not authenticated
// Example from https://authjs.dev/getting-started/session-management/protecting
if (!req.auth && !req.nextUrl.pathname.startsWith("/auth")) {
const newUrl = new URL("/auth/signin", req.nextUrl.origin);
return Response.redirect(newUrl);
}

// Otherwise return security headers
return securityHeaders();
});

export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};