Skip to content

Commit

Permalink
upgraded to next.js 13, bumped version number
Browse files Browse the repository at this point in the history
  • Loading branch information
amorey committed Dec 23, 2022
1 parent 55a1fce commit 63f2bdf
Show file tree
Hide file tree
Showing 37 changed files with 1,758 additions and 4,856 deletions.
52 changes: 17 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ This library uses the cookie strategy from [expressjs/csurf](https://github.com/

# Features

- Supports Next.js 13
- Runs in edge runtime
- Implements cookie strategy from [expressjs/csurf](https://github.com/expressjs/csurf) and the crypto logic from [pillarjs/csrf](https://github.com/pillarjs/csrf)
- Gets token from HTTP request header (`x-csrf-token`) or from request body field (`csrf_token`)
- Gets token from HTTP request header (`X-CSRF-Token`) or from request body field (`csrf_token`)
- Handles form-urlencoded or json-encoded HTTP request bodies
- Customizable cookie options
- TypeScript definitions included
Expand All @@ -19,6 +20,7 @@ To use Edge-CSRF, first add it as a dependency to your app:

```bash
npm install edge-csrf
yarn add edge-csrf
```

Next, create a middleware file (`middleware.ts`) for your project and add the Edge-CSRF middleware:
Expand All @@ -31,7 +33,11 @@ import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

// initalize protection function
const csrfProtect = csrf();
const csrfProtect = csrf({
cookie: {
secure: false // WARNING: set this to `true` in production!
}
});

export async function middleware(request: NextRequest) {
const response = NextResponse.next();
Expand All @@ -41,52 +47,31 @@ export async function middleware(request: NextRequest) {

// check result
if (csrfError) {
const url = request.nextUrl.clone();
url.pathname = '/api/csrf-invalid';
return NextResponse.rewrite(url);
return new NextResponse('invalid csrf token', { status: 403 });
}

return response;
}
```

Next, create a handler to return CSRF error messages to the user:

```typescript
// pages/api/csrf-invalid.ts

import type { NextApiRequest, NextApiResponse } from 'next';

type Data = {
status: string
};

export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
res.status(403).send({ status: 'invalid csrf token' });
}
```

Now, all HTTP submission requests (e.g. POST, PUT, DELETE, PATCH) will be rejected if they do not include a valid CSRF token. To add the CSRF token to your forms, you can fetch it from the `x-csrf-token` HTTP response header server-side or client-side. For example:
Now, all HTTP submission requests (e.g. POST, PUT, DELETE, PATCH) will be rejected if they do not include a valid CSRF token. To add the CSRF token to your forms, you can fetch it from the `X-CSRF-Token` HTTP response header server-side or client-side. For example:

```typescript
// pages/form.ts

import type { GetServerSideProps } from 'next';
import type { NextPage, GetServerSideProps } from 'next';
import React from 'react';

type Props = {
csrfToken: string
csrfToken: string;
};

export const getServerSideProps: GetServerSideProps = async ({ res }) => {
const csrfToken = res.getHeader('x-csrf-token');
const csrfToken = res.getHeader('x-csrf-token') || 'missing';
return { props: { csrfToken } };
}

const FormPage: React.FunctionComponent<Props> = ({ csrfToken }) => {
const FormPage: NextPage<Props> = ({ csrfToken }) => {
return (
<form action="/api/form-handler" method="post">
<input type="hidden" value={csrfToken}>
Expand All @@ -108,11 +93,8 @@ type Data = {
status: string
};

export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
// this code won't execute unless CSRF token passes validation
export default function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
// this code won't execute unless CSRF token passes validation
res.status(200).json({ status: 'success' });
}
```
Expand Down Expand Up @@ -149,7 +131,7 @@ Here are the default configuration values:
saltByteLength: 8,
secretByteLength: 18,
token: {
responseHeader: 'x-csrf-token',
responseHeader: 'X-CSRF-Token',
value: undefined
}
}
Expand Down
35 changes: 0 additions & 35 deletions example-ts/.gitignore

This file was deleted.

10 changes: 6 additions & 4 deletions example-ts/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import csrf from 'edge-csrf';
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

const csrfProtect = csrf();
const csrfProtect = csrf({
cookie: {
secure: false // WARNING: set this to `true` in production!
}
});

export async function middleware(request: NextRequest) {
const response = NextResponse.next();
Expand All @@ -12,9 +16,7 @@ export async function middleware(request: NextRequest) {

// check result
if (csrfError) {
const url = request.nextUrl.clone();
url.pathname = '/api/csrf-invalid';
return NextResponse.rewrite(url);
return new NextResponse('invalid csrf token', { status: 403 });
}

return response;
Expand Down
3 changes: 3 additions & 0 deletions example-ts/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
experimental: {
allowMiddlewareResponseBody: true,
},
}

module.exports = nextConfig
19 changes: 8 additions & 11 deletions example-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,14 @@
"lint": "next lint"
},
"dependencies": {
"edge-csrf": "0.2.1",
"next": "12.2.2",
"@types/node": "18.11.15",
"@types/react": "18.0.26",
"@types/react-dom": "18.0.9",
"eslint": "8.29.0",
"eslint-config-next": "13.0.6",
"next": "13.0.6",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@types/node": "18.0.3",
"@types/react": "18.0.15",
"@types/react-dom": "18.0.6",
"eslint": "8.19.0",
"eslint-config-next": "12.2.2",
"typescript": "4.7.4"
"react-dom": "18.2.0",
"typescript": "4.9.4"
}
}
6 changes: 6 additions & 0 deletions example-ts/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import '../styles/globals.css'
import type { AppProps } from 'next/app'

export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
6 changes: 0 additions & 6 deletions example-ts/pages/api/csrf-invalid.js

This file was deleted.

12 changes: 0 additions & 12 deletions example-ts/pages/api/csrf-invalid.ts

This file was deleted.

6 changes: 0 additions & 6 deletions example-ts/pages/api/form-handler.js

This file was deleted.

7 changes: 2 additions & 5 deletions example-ts/pages/api/form-handler.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import type { NextApiRequest, NextApiResponse } from 'next';

type Data = {
status: string
status: string;
};

export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
export default function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
res.status(200).json({ status: 'success' });
}
10 changes: 5 additions & 5 deletions example-ts/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import type { GetServerSideProps } from 'next';
import type { NextPage, GetServerSideProps } from 'next';
import React from 'react';

type Props = {
csrfToken: string
csrfToken: string;
};

export const getServerSideProps: GetServerSideProps = async ({ res }) => {
const csrfToken = res.getHeader('x-csrf-token');
const csrfToken = res.getHeader('X-CSRF-Token') || 'missing';
return { props: { csrfToken } };
}

const Home: React.FunctionComponent<Props> = ({ csrfToken }) => {
const Home: NextPage<Props> = ({ csrfToken }) => {
return (
<>
<p>CSRF token value: {csrfToken || 'missing'}</p>
<p>CSRF token value: {csrfToken}</p>
<form action="/api/form-handler" method="post">
<legend>Form without CSRF (should fail):</legend>
<input type="text" name="input1" />
Expand Down
Binary file added example-ts/public/favicon.ico
Binary file not shown.
4 changes: 4 additions & 0 deletions example-ts/public/vercel.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 63f2bdf

Please sign in to comment.